package cheme.ProcessEditor.Parts;


import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.EventDispatcher;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.SharedCursors;
import org.eclipse.jface.util.Assert;

import cheme.ProcessEditor.Figures.AnnotationFig;
import cheme.ProcessEditor.Figures.IDecoratable;
import cheme.ProcessEditor.Figures.IDecoratable.IDecorator;
import cheme.ProcessModel.IModelElement;
import cheme.ProcessModel.IPropertyAware;
import org.eclipse.draw2d.ToolTipHelper;



/*
 * Changes from V2 to V3
 * Added this class.
 * 
 * With the addition of edit policies to the plugin, the edit parts need to 
 * impleement a basic observer pattern in order to be notified about the 
 * commands that resutl from requests sent to edit policies.
 * 
 *  Which objects are the ators in theis pattern instance?
 *  
 *  Anyway, the commonalities of this command listening are factored out into an
 *  edit part base class: EditPartWithListener
 *  
 *  Of course commands are ultimately directed to the underlying model and so we
 *  set up a base model class as well that has a generic property change 
 *  mechanism as well--this is AbstractModel.
 *  
 *  Changes from v3 to v4
 *  None.
 */
public abstract class PropertyAwarePart extends ChemeEditPart implements PropertyChangeListener
{
	protected List<AnnotationPart> anotations = new ArrayList<AnnotationPart>();
	
	public List<AnnotationPart> getAnnotations()
	{
		if(null == anotations)
		{
			anotations = new ArrayList<AnnotationPart>();
		}
		
		return anotations;
	}
	
	/**
	 * @see org.eclipse.gef.EditPart#activate()
	 */
	public void activate()
	{
		super.activate();
		
		List list = getAnnotations();
		for (int i = 0; i < list.size(); i++)
		{
			((EditPart)list.get(i)).activate();
		}
		
		IPropertyAware propertyAwareObject = (IPropertyAware)getModel();
		propertyAwareObject.addPropertyChangeListener(this);
	}

	public void addNotify()
	{
		register();
		createEditPolicies();
		
		List list = getChildren();
		for (int i = 0; i < list.size(); i++)
		{
			((EditPart)list.get(i)).addNotify();
		}
		
		list = getAnnotations();
		for (int i = 0; i < list.size(); i++)
		{
			((EditPart)list.get(i)).addNotify();
		}
		
		refresh();
	}
	
	/**
	 * @see org.eclipse.gef.EditPart#deactivate()
	 */
	public void deactivate()
	{
		List list = getAnnotations();
		for (int i = 0; i < list.size(); i++)
		{
			((EditPart)list.get(i)).deactivate();
		}
		
		super.deactivate();
		
		
		IPropertyAware propertyAwareObject = (IPropertyAware) getModel();
		propertyAwareObject.removePropertyChangeListener(this);
	}
	
	/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 * If the whole Annotations as first class elements ever goes through, will 
	 * probably need to specialize protected void refreshChildren() 
	 */

	/**
	 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
	 */
	public void propertyChange(PropertyChangeEvent evt)
	{
		try
		{
			String property = evt.getPropertyName();

			if (IPropertyAware.P_INTERPRETABLE.equals(property))
			{
				handleInterpretation(evt);
			}
			else if (IPropertyAware.P_INITIAL.equals(property))
			{
				handleInitialization(evt);
			}
			else if (IPropertyAware.P_DISPOSE.equals(property))
			{
				handleDispose(evt);
			}
			else if (IPropertyAware.P_ANNOTATION.equals(property))
			{
				handleChildChange(evt); // PA Commented
//				handleAnnotationChange(evt);
			}
//			else if (IPropertyAware.P_ANNOTATIONS.equals(property))
//			{
//				handleChildrenChange(evt);
//			}
			else if (IPropertyAware.P_CHILD.equals(property))
			{
				handleChildChange(evt);
			}
			else if (IPropertyAware.P_CHILDREN.equals(property))
			{
				handleChildrenChange(evt);
			}
			else if (IPropertyAware.P_SOURCE_STREAM.equals(property))
			{
				handleSourceChange(evt);
			}
			else if (IPropertyAware.P_SINK_STREAM.equals(property))
			{
				handleTargetChange(evt);
			}
			else if (IPropertyAware.P_CONSTRAINT.equals(property))
			{
				handleBoundsChange(evt);
			}
			if (IPropertyAware.P_REORDER.equals(property))
			{
				handleReorderChange(evt);
			}
			else if (IPropertyAware.P_TEXT.equals(property))
			{
				handleTextChange(evt);
			}
			else if (IPropertyAware.P_LAYOUT.equals(property))
			{
				handleLayoutChange(evt);
			}
			else if(IPropertyAware.P_COMPLETENESS.equals(property))
			{
				handleCompletenessChange(evt);
			}
			else if (IPropertyAware.P_COLOR.equals(property))
			{
				handleColorChange(evt);
			}
			else if (IPropertyAware.P_CLEAR.equals(property))
			{
				handleClearChange(evt);
			}
			
			else if (IPropertyAware.P_BACKGROUNDCOLOR.equals(property))
			{
				handleBackGroundChangeEvent(evt);				
			}
			else if (IPropertyAware.P_VISIBILITY.equals(property))
			{
				handleVisibilityChange(evt);
			}
			else if (IPropertyAware.P_LOCATION.equals(property))
			{
				handleLocationChange(evt);
			}

			//we want direct edit name changes to update immediately
			//not use the Graph animation, if automatic layout is being used
			if (IPropertyAware.P_TEXT.equals(property))
			{
				GraphicalEditPart graphicalEditPart = (GraphicalEditPart) (getViewer().getContents());
				IFigure partFigure = graphicalEditPart.getFigure();
				partFigure.getUpdateManager().performUpdate();
			}
			/*if (IPropertyAware.P_COLOR.equals(property))
			{
				GraphicalEditPart graphicalEditPart = (GraphicalEditPart) (getViewer().getContents());
				IFigure partFigure = graphicalEditPart.getFigure();
				partFigure.getUpdateManager().performUpdate();
			}*/
			
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleLocationChange(PropertyChangeEvent evt) {
		// TODO Auto-generated method stub
		
	}

	protected void handleVisibilityChange(PropertyChangeEvent evt) 
	{
		
		
	}

	protected void handleClearChange(PropertyChangeEvent evt)
	{	
		
	}

	protected void handleInterpretation(PropertyChangeEvent evt)
	{
	}
	
	protected void handleInitialization(PropertyChangeEvent evt)
	{
	}
	
	protected void handleDispose(PropertyChangeEvent evt)
	{
	}
	
	/**
	 * Called when change to one of the inputs occurs
	 */
	protected void handleTargetChange(PropertyChangeEvent evt)
	{
		/*
		 * Something like refreshTargetConnections() works but is not efficient.
		 * A more efficient implementation should either remove or add the relevant target connection using the removeTargetConnection(ConnectionEditPart connection) or addTargetConnection(ConnectionEditPart connection, int index)
		 */
 
		Object newValue = evt.getNewValue();
		Object oldValue = evt.getOldValue();

		if (!((oldValue != null) ^ (newValue != null)))
		{
			throw new IllegalStateException("Exactly one of old or new values must be non-null for INPUT event");
		}

		if (newValue != null)
		{
			//add new connection
			ConnectionEditPart editPart = createOrFindConnection(newValue);
			int modelIndex = getModelTargetConnections().indexOf(newValue);
			addTargetConnection(editPart, modelIndex);
		}
		else
		{
			//remove connection
			List children = getTargetConnections();
			ConnectionEditPart partToRemove = null;

			for(Object part : children)
			{
				if (((ConnectionEditPart)part).getModel() == oldValue)
				{
					partToRemove = (ConnectionEditPart)part;
					break;
				}
			}

			if (partToRemove != null)
			{
				removeTargetConnection(partToRemove);
			}
		}
		
		getContentPane().revalidate();
	}
	
	protected void handleBackGroundChangeEvent(PropertyChangeEvent evt)
	{		
		Object oldValue = evt.getOldValue();
		
				
		//IFigure partFigure1 = this.getContentPane();
		//partFigure1.setForegroundColor(ColorConstants.blue);
		//partFigure1.setFocusTraversable(true);
		//partFigure1.setCursor(SharedCursors.HELP);		
		// Thread didn;'t work
		/*AnimationThread thread = new AnimationThread();
		thread.setObject(partFigure1);
	    thread.start();*/

		/*long currentTimeInSeconds = System.currentTimeMillis()/1000;
		int color = 0;
		long loopLimit = currentTimeInSeconds + 10 ;
		for(long i= currentTimeInSeconds; i < loopLimit ; i= (System.currentTimeMillis()/1000))
		{
			if(color == 0)
			{
				partFigure1.setForegroundColor(ColorConstants.blue);
				
				color = 1;
			}
			else
			{
				partFigure1.setForegroundColor(ColorConstants.red);
				color = 0;
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}*/
		//partFigure1.setForegroundColor(ColorConstants.red);
	
		//partFigure1.setBackgroundColor(ColorConstants.red);
		//this.getContentPane().revalidate();
		/*partFigure1.getUpdateManager().performUpdate();
		((GraphicalEditPart)getParent()).setLayoutConstraint(this, getFigure(), constraint);*/
		
		//super.refreshChildren();
		/*partFigure1.getUpdateManager().performUpdate();
		GraphicalEditPart graphicalEditPart = (GraphicalEditPart) (this.getViewer().getContents());
	
		List children = graphicalEditPart.getChildren();
		
		IFigure partFigure = graphicalEditPart.getFigure();
		partFigure.setBackgroundColor(ColorConstants.blue);*/
		
	
		//this.setFocus(true);	
	}
	

	/**
	 * Called when change to one of the outputs occurs
	 */
	protected void handleSourceChange(PropertyChangeEvent evt)
	{
		/*
		 * Something like refreshSourceConnections() works but is not efficient.
		 * A more efficient implementation should either remove or add the relevant target connection using the removeSourceConnection(ConnectionEditPart connection) or addSourceConnection(ConnectionEditPart connection, int index)
		 */
		
		Object newValue = evt.getNewValue();
		Object oldValue = evt.getOldValue();

		if (!((oldValue != null) ^ (newValue != null)))
		{
			throw new IllegalStateException("Exactly one of old or new values must be non-null for INPUT event");
		}

		if (newValue != null)
		{
			//add new connection
			ConnectionEditPart editPart = createOrFindConnection(newValue);
			int modelIndex = getModelSourceConnections().indexOf(newValue);
			addSourceConnection(editPart, modelIndex);
		}
		else
		{
			//remove connection
			List children = getSourceConnections();
			ConnectionEditPart partToRemove = null;

			for(Object part : children)
			{
				if (((ConnectionEditPart)part).getModel() == oldValue)
				{
					partToRemove = (ConnectionEditPart)part;
					break;
				}
			}

			if (partToRemove != null)
			{
				removeSourceConnection(partToRemove);
			}
		}
		
		getContentPane().revalidate();
		
	}

	protected void addAnnotationVisual(EditPart childEditPart, int index)
	{
		/*
		 * IF I am decoratable and the child is a decoration
		 */
		
		if(getFigure() instanceof IDecoratable)
		{
			((IDecoratable)figure).add((IDecorator)((GraphicalEditPart)childEditPart).getFigure());
		}
	}
	
	protected void removeAnnotationVisual(EditPart childEditPart)
	{
		/*
		 * IF I am decoratable and the child is a decoration
		 */
		if(getFigure() instanceof IDecoratable)
		{
			((IDecoratable)figure).remove((IDecorator)((GraphicalEditPart)childEditPart).getFigure());
		}
	}
	
	protected void addAnnotation(EditPart child, int index)
	{
		Assert.isNotNull(child);

//		if (index == -1)
//		{
//			index = getChildren().size();
//		}
		
//		if (children == null)
//		{
//			children = new ArrayList(2);
//		}
		
		index = -1;

		getAnnotations().add((AnnotationPart)child);
		child.setParent(this);
		addAnnotationVisual(child, index);
		child.addNotify();

		if (isActive())
		{
			child.activate();
		}
		
		fireChildAdded(child, index);
	}
	
	protected void removeAnnotation(EditPart child)
	{
		Assert.isNotNull(child);
		
		int index = getAnnotations().indexOf(child);
		
		if (index < 0)
		{
			return;
		}
		
		fireRemovingChild(child, index);
		
		if (isActive())
		{
			child.deactivate();
		}
		
		child.removeNotify();
		removeAnnotationVisual(child);
		child.setParent(null);
		getAnnotations().remove(child);
	}
	
	@Override
	protected void addChildVisual(EditPart childEditPart, int index)
	{
		if(((GraphicalEditPart)childEditPart).getFigure() instanceof IDecorator)
		{
			addAnnotationVisual(childEditPart, index);
		}
		else
		{
			super.addChildVisual(childEditPart, index);
		}
	}
	
	@Override
	protected void removeChildVisual(EditPart childEditPart)
	{		
		if(((GraphicalEditPart)childEditPart).getFigure() instanceof IDecorator)
		{
			removeAnnotationVisual(childEditPart);
		}
		else
		{
			super.removeChildVisual(childEditPart);
		}
	}
	
	/**
	 * called when an annotation added or removed
	 */
	protected void handleAnnotationChange(PropertyChangeEvent evt)
	{
		Object newValue = evt.getNewValue();
		Object oldValue = evt.getOldValue();

		if (!((oldValue != null) ^ (newValue != null)))
		{
			throw new IllegalStateException("Exactly one of old or new values must be non-null for CHILD event");
		}
		
		if(newValue != null) // add new child
		{
			/*
			 * When adding our child, we need to call createChild() to get a 
			 * new EditPart for the model child. We then find the index of the 
			 * model child in the containing List, and add our new child 
			 * EditPart using this index . 
			 */
			
			addAnnotation(createChild(newValue), -1);
		}
		else // remove an existing child
		{
			/*
			 * When removing a child, we iterate through the existing children 
			 * EditParts until we find the one representing the removed model 
			 * child. We then remove this EditPart.
			 */
			
			List<AnnotationPart> children = getAnnotations();
			AnnotationPart partToRemove = null;

			for(AnnotationPart part : children)
			{
				if(part.getModel() == oldValue)
				{
					partToRemove = part;
					break;
				}
			}
			
			if(partToRemove != null)
			{
				removeAnnotation(partToRemove);
			}
		}
	}
	
//	/**
//	 * called when two or more annotations added or removed
//	 */
//	protected void handleAnnotationsChange(PropertyChangeEvent evt)
//	{
//	}
	
	protected void handleChildrenChange(PropertyChangeEvent evt)
	{
		refresh();
	}
	
	/**
	 * called when child added or removed
	 */
	protected void handleChildChange(PropertyChangeEvent evt)
	{
		Object newValue = evt.getNewValue();
		Object oldValue = evt.getOldValue();

		if (!((oldValue != null) ^ (newValue != null)))
		{
			throw new IllegalStateException("Exactly one of old or new values must be non-null for CHILD event");
		}
		
		if(newValue != null) // add new child
		{
			/*
			 * When adding our child, we need to call createChild() to get a 
			 * new EditPart for the model child. We then find the index of the 
			 * model child in the containing List, and add our new child 
			 * EditPart using this index . 
			 */
			
			EditPart editPart = createChild(newValue);
			int modelIndex = getModelChildren().indexOf(newValue);
			addChild(editPart, modelIndex);
		}
		else // remove an existing child
		{
			/*
			 * When removing a child, we iterate through the existing children 
			 * EditParts until we find the one representing the removed model 
			 * child. We then remove this EditPart.
			 */
			
			List<? extends EditPart> children = getChildren();
			EditPart partToRemove = null;

			for(EditPart part : children)
			{
				if(part.getModel() == oldValue)
				{
					partToRemove = part;
					break;
				}
			}
			
			if(partToRemove != null)
			{
				removeChild(partToRemove);
			}
		}
	}

	/**
	 * Called when columns are re-ordered within
	 */
	protected void handleReorderChange(PropertyChangeEvent evt)
	{
		refreshChildren();
		refreshVisuals();
	}

	/**
	 * @param subclass decides what to do if layout property event is fired
	 */
	protected void handleLayoutChange(PropertyChangeEvent evt)
	{
	}

	/**
	 * handles change in bounds, to be overridden by subclass
	 */
	protected void handleBoundsChange(PropertyChangeEvent evt)
	{
	}
	
	/**
	 * Handles change in name - to be overridden by subclasses
	 */
	protected void handleTextChange(PropertyChangeEvent evt)
	{
	}
	
	/**
	 * Handles change in name - to be overridden by subclasses
	 */
	protected void handleColorChange(PropertyChangeEvent evt)
	{
	}
	
	/**
	 * handles change in completeness of a stream descriptor, to be overridden by subclass
	 */
	protected void handleCompletenessChange(PropertyChangeEvent evt)
	{
	}
}
